#include "csbbind.h"
#include "csbmath.h"
#include "csb.h"

NS_CSB_BEGIN

/*********************| 绑定Ｃ的函数 |*********************/
static void _bind_nodeBeginCB( csnode_t* pcs, void* udata)
{
	(void)pcs;
	struct BindProto::Bind2ELE_t* pb = (struct BindProto::Bind2ELE_t*)(udata);
	pb->b->proto->onBegin(pb->b->fname.c_str(), pb->e);
}

static void _bind_nodeEndCB( csnode_t* pcs , void* udata)
{
	(void)pcs;
	struct BindProto::Bind2ELE_t* pb = (struct BindProto::Bind2ELE_t*)(udata);
	pb->b->proto->onEnd(pb->b->fname.c_str(), pb->e);
}

static void _bind_objUpdateCB( csobj_t* po , void* udata)
{
	struct BindProto::Bind2ELE_t* pb = (struct BindProto::Bind2ELE_t*)(udata);
	pb->b->proto->onObjUpdate(pb->b->fname.c_str(), pb->e);
}

static void _bind_buBornCB(bullet_t* p, void* udata)
{
	struct BindProto::Bind2ELE_t* pb = (struct BindProto::Bind2ELE_t*)(udata);
	pb->b->proto->onBulletBorn(pb->b->fname.c_str(), p, pb->e);
}

static void _bind_buDeadCB(bullet_t* p, void* udata)
{
	struct BindProto::Bind2ELE_t* pb = (struct BindProto::Bind2ELE_t*)(udata);
	pb->b->proto->onBulletDead(pb->b->fname.c_str(), p, pb->e);
}

static int _bind_buUpdateCB(bullet_t* p, void* udata)
{
	struct BindProto::Bind2ELE_t* pb = (struct BindProto::Bind2ELE_t*)(udata);
	return pb->b->proto->onBulletUpdate(pb->b->fname.c_str(), p, pb->e);
}

static void _bind_autoMachine(pos_t* out, void* udata)
{
	struct BindProto::Bind2ELE_t* pb = (struct BindProto::Bind2ELE_t*)(udata);
	pb->b->proto->onAutoMachine(pb->b->fname.c_str(), pb->e, out);
}
/*********************| BindProto functions |*********************/
/* 加载文件的时候用来插入数据的方法 */
BindProto::BindProto(BindHelperProto* h, void* pobj) : _helper(h), _bindObj(pobj)
{}

BindProto::~BindProto()
{
	_bindObj = NULL;
}

BindProto::bind_t*BindProto::newBind(BindProto* p, const char* fname) {
	bind_t* r = new bind_t;
	r->proto = p;
	if (fname) {
		r->fname = fname;
	}
	return r;
}

/* 生死判定 */
void BindProto::liveJudge(BindProto::bindele_t* pb,  const size_t j1)
{
	if (pb && pb->pcs->bulist.size <= j1 && CSB_FLAG_TEST(pb->pcs->ctlflag, NODE_CTL_STOP_EMIT) &&
		 CSB_FLAG_TEST(pb->pcs->ctlflag, NODE_CTL_STOP_OBJ) ) {
		pb->pcs->ctlflag = CSB_FLAG_OFF(pb->pcs->ctlflag, NODE_CTL_STOP_BULLET);
		pb->live = 0;
	}
}

int BindProto::remove_bu_node_fun(void** datare, void* flag)
{
	bullet_t* b = (bullet_t*)(*datare);
	if (b) {
		BindHelperProto::BuRecyleHelper_t* helper = (BindHelperProto::BuRecyleHelper_t*)flag;
		if (b->udata) {
			helper->helper->recycleBullet(b->udata, b->attr.ID, helper->fname, helper->objdataName);
		}
	}
	return 1;
}

BindProto::bindele_t* BindProto::getEleN(BindProto::bind_t* pb, size_t n)
{
	if (pb && n < pb->eleList.size()) {
		std::list<BindProto::bindele_t*>::iterator it = pb->eleList.begin();
		while (n > 0) {
			--n;
			++it;
		}
		return (bindele_t*)(*it);
	}
	return NULL;
}

void BindProto::unbindMe(bind_t* pb, const int flag)
{
	bool final = CSB_FLAG_TEST(flag, BindProto::FINAL);
	if (pb && pb->proto == this && (_bindObj || final)) {
		BindProto::bindele_t* pe = NULL;
		if (final) {
			// remove all bullets node
			struct Bind2ELE_t* p2;
			struct BindHelperProto::BuRecyleHelper_t recyleHelper;
			recyleHelper.fname = pb->fname.c_str();
			recyleHelper.helper = _helper;
			for (std::list<BindProto::bindele_t*>::iterator it = pb->eleList.begin();
			     it != pb->eleList.end(); ++it) {
				pe = *it;
				p2 = (struct Bind2ELE_t*)pe->pcs->cbs.udata;
				recyleHelper.objdataName = pe->objdatana;
				list_foreach(&pe->pcs->bulist, BindProto::remove_bu_node_fun,(void*)(&recyleHelper));
				CSB_SAFE_DELETE(p2);
				csnode_buf_return(pe->pcs);
				CSB_SAFE_DELETE(pe);
			}
			pb->eleList.clear();
		} else {
			for (std::list<BindProto::bindele_t*>::iterator it = pb->eleList.begin();
			     it != pb->eleList.end(); ++it) {
				pe = *it;
				pe->flag |= flag;
				pe->pcs->ctlflag = CSB_FLAG_ON(pe->pcs->ctlflag, NODE_CTL_STOP_OBJ | NODE_CTL_STOP_EMIT);
				liveJudge(pe, 0);
			}
		}
		bindObjRelease();
		_bindObj = NULL;
	}
}

void BindProto::clearBulletsOnce(BindProto::bind_t* pb)
{
	if (pb) {
		BindProto::bindele_t* pe = NULL;
		struct BindHelperProto::BuRecyleHelper_t recyleHelper;
		recyleHelper.fname = pb->fname.c_str();
		recyleHelper.helper = _helper;
		for (std::list<BindProto::bindele_t*>::iterator eit = pb->eleList.begin();
		     eit != pb->eleList.end(); ++eit) {
			pe = *eit;
			if (pe->pcs->bulist.size) {
				recyleHelper.objdataName = pe->objdatana;
				list_foreach(&pe->pcs->bulist, BindProto::remove_bu_node_fun,(void*)(&recyleHelper));
				csnode_zero_bulist(pe->pcs);
			}
		}
	}
}

void BindProto::onBegin(const char* fname, bindele_t* p)
{
	updateEleBase(p);
}

void BindProto::onEnd(const char* fname, bindele_t* p)
{
	_helper->objEndCB(p);
}

void BindProto::onObjUpdate(const char* fname, bindele_t* p)
{
	updateEleBase(p);
}

void* BindProto::onBulletBorn(const char* fname, bullet_t* p, BindProto::bindele_t* pb)
{
	if (CSB_FLAG_TEST(pb->flag, BindProto::FINAL))
		return NULL;
	if (CSB_FLAG_TEST(pb->flag, BindProto::RM_BULLET_ATONCE))
		return NULL;
	pb->flag = CSB_FLAG_ON(pb->flag,RBRT_AM);
	// 子弹出生的时候利用emitBullet获得一个计算节点
	void* pn = _helper->emitBullet(p->attr.ID, fname, pb->objdatana);
	p->udata = pn;
	if (pn) {
		updateBulletWithEle(p->ele,pn);
	}
	return pn;
}

void BindProto::onBulletDead(const char* fname, bullet_t* p, bindele_t* pb)
{
	if (p->udata) {
		_helper->recycleBullet(p->udata, p->attr.ID, fname, pb->objdatana);
		liveJudge(pb, 1);
	}
}

int BindProto::onBulletUpdate(const char* fname, bullet_t* p, bindele_t* pb)
{
	// 处理立刻删除活着的子弹的标志
	if (CSB_FLAG_TEST(pb->flag, BindProto::RM_BULLET_ATONCE))
		return 2;
	if (CSB_FLAG_TEST(pb->flag, BindProto::FINAL))
		return 2;
	// 获取子弹，修改的子弹Node的信息
	if (p->udata) {
		updateBulletWithEle(p->ele, p->udata);
	}

	// 子弹碰撞的判定
	BindHelperProto::BulletStateCheck_t state = {
	        p->udata, p->attr.ID, fname, pb->objdatana, &p->ele
	};
	return _helper->bulletStateCheck(state);
}

void BindProto::onAutoMachine(const char* fname, BindProto::bindele_t* b, pos_t* pos)
{
	_helper->autoMachine(pos);
	pos->x = COORD_X_CONVERT(pos->x);
	pos->y = COORD_Y_CONVERT(pos->y);
}

/*********************| CSBBindMan functions |*********************/
/* 活取csb文件的文件名去掉扩展名的部分 */
void CSBBindMan::csbfilename_head(const char* src, char* out)
{
	size_t l = strlen(src);
	while (l) {
		--l;
		if ('.' == src[l])
			break;
	}
	size_t t = 0;
	while (t < l) {
		if ('/' == src[t])
			break;
		++t;
	}
	++t;
	if (t >= l) {
		strncpy(out, src , l);
	} else {
		l -= t;
		strncpy(out, src + t, l);
	}
	out[l] = 0;
}

int CSBBindMan::addobjdata_func(void** datare, void* flag)
{
	csobjdata_t* p = (csobjdata_t*)(*datare);
	((std::map<OBJ_DATA_MAP_TYPE>*)(flag))->insert(std::make_pair(std::string(p->name), p));
	return 1;
}



bool CSBBindMan::_inited = false;

CSBBindMan::CSBBindMan()
{}

CSBBindMan::~CSBBindMan()
{ }

bool CSBBindMan::init(float designW, float designH)
{
	CSB_YES_RET(_inited, true);
	_inited = csb_common_init(designW, designH);
	return _inited;
}

bool CSBBindMan::end()
{
	if (_inited) {
		csb_common_end(false);
		_inited = false;
		return true;
	}
	return false;
}

void CSBBindMan::CSMakeObjDataName(const int etype, const uint32_t NO, char* out, const size_t outlen)
{
	if (outlen > 0) {
		memset(out, 0, outlen);
		const char* en = "";
		switch (etype) {
		case EMITTER:
			en = "em";
			break;
		case LASER:
			en = "la";
			break;
		}
		snprintf(out, outlen, "%s#%u", en, NO);
	}
}

void CSBBindMan::updateOnce()
{
	BindProto::bind_t* p = NULL;
	if (_bindSet.empty()) return;
	BindProto::bindele_t* pe = NULL;
	struct BindProto::Bind2ELE_t* p2 = NULL;
	std::list<BindProto::bindele_t*>::iterator eit;
	for (std::set<BindProto::bind_t*>::iterator it = _bindSet.begin();
	     it != _bindSet.end();) {
		p = *it;
		for(eit = p->eleList.begin(); eit != p->eleList.end();) {
			pe = *eit;
			if (pe->live) {
				if (CSB_FLAG_TEST(pe->flag, BindProto::RUNNING)) {
					csnode_update(pe->pcs);
				}
				++eit;
			} else {
				p2 = (struct BindProto::Bind2ELE_t*)pe->pcs->cbs.udata;
				CSB_SAFE_DELETE(p2);
				csnode_buf_return(pe->pcs);
				CSB_SAFE_DELETE(pe);
				eit = p->eleList.erase(eit);
			}
		}
		if(p->eleList.empty()) {
			_bindSet.erase(it++);
			delete p->proto;
			delete p;
		} else {
			++it;
		}

	}

}

bool CSBBindMan::loadfile(const char* path)
{
	CSB_NO_RET(path, false);
	// 判断是否已经加载过此文件
	char fh[256] = {0};
	csbfilename_head(path, fh);
	std::map<std::string, std::map<OBJ_DATA_MAP_TYPE>* >::iterator it = _objsMap.find(fh);
	CSB_YES_RET(_objsMap.end() != it,  true);
	// 加载此文件
	unsigned char* data = loadCSBFileData(path);
	list_t* objlist = load_csbdata(data);
	if (data) {
		delete []data;
	}
	if (objlist && objlist->size > 0) {
		std::map<OBJ_DATA_MAP_TYPE>* om = new std::map<OBJ_DATA_MAP_TYPE>();
		_objsMap.insert(std::make_pair(std::string(fh), om));
		// foreach read data
		list_foreach(objlist, CSBBindMan::addobjdata_func, (void*)om);
		list_free(objlist, NULL);
		return true;
	}
	return false;
}

bool CSBBindMan::loadfile(const std::vector<const char*>& filesVec)
{
	for (size_t i = 0; i < filesVec.size(); ++i) {
		if (!loadfile(filesVec[i]))
			return false;
	}
	return true;
}

void CSBBindMan::unloadobjdata(const char* fname, const char* objdatana)
{
	CSB_NO_VRET(fname && objdatana);
	std::map<std::string , std::map<OBJ_DATA_MAP_TYPE>* >::iterator it = _objsMap.find(fname);
	CSB_YES_VRET(_objsMap.end() == it);
	std::map<OBJ_DATA_MAP_TYPE>* pm = it->second;
	std::map<OBJ_DATA_MAP_TYPE>::iterator pit = pm->find(objdatana);
	if (pm->end() != pit) {
		csobjdata_free(pit->second);
		pm->erase(pit);
	}
	if (pm->empty()) {
		delete pm;
		_objsMap.erase(it);
	}
}

void CSBBindMan::unloadfile(const char* fname)
{
	CSB_NO_VRET(fname);
	std::map<std::string , std::map<OBJ_DATA_MAP_TYPE>* >::iterator it = _objsMap.find(fname);
	CSB_YES_VRET(_objsMap.end() == it);
	std::map<OBJ_DATA_MAP_TYPE>* pm = it->second;
	for (std::map<OBJ_DATA_MAP_TYPE>::iterator pit = pm->begin(); pit != pm->end(); ++pit) {
		csobjdata_free(pit->second);
	}
	delete pm;
	_objsMap.erase(it);
}

void CSBBindMan::unloadAll()
{
	std::map<OBJ_DATA_MAP_TYPE>* pm = NULL;
	std::map<OBJ_DATA_MAP_TYPE>::iterator pit;
	for(std::map<std::string, std::map<OBJ_DATA_MAP_TYPE>* >::iterator it = _objsMap.begin();
	    it != _objsMap.end(); ++it) {
		pm = it->second;
		for (pit = pm->begin(); pit != pm->end(); ++pit) {
			csobjdata_free(pit->second);
		}
		delete pm;
	}
	_objsMap.clear();
}

void CSBBindMan::loadFilesInfo(std::vector<CSBBindMan::LoadFilesInfo>& v)
{
	v.clear();
	struct LoadFilesInfo tmp;
	std::map<OBJ_DATA_MAP_TYPE>* pm = NULL;
	for(std::map<std::string, std::map<OBJ_DATA_MAP_TYPE>* >::iterator it = _objsMap.begin();
	    it != _objsMap.end(); ++it) {
		tmp.fname = it->first;
		pm = it->second;
		tmp.objn = pm->size();
		v.push_back(tmp);
	}
}

bool CSBBindMan::bind(BindProto::bind_t* pb)
{
	CSB_NO_RET(pb && pb->eleList.size(), false);
	// 获取csobjdata_t
	std::map<std::string , std::map<OBJ_DATA_MAP_TYPE>* >::iterator it = _objsMap.find(pb->fname);
	CSB_YES_RET(_objsMap.end() == it, false);
	std::map<OBJ_DATA_MAP_TYPE>* pm = it->second;
	// 检测是否存在已经绑定的节点
	std::map<OBJ_DATA_MAP_TYPE>::iterator pit ;
	BindProto::bindele_t* pele = NULL;
	csobjdata_t* pd = NULL;
	struct BindProto::Bind2ELE_t* p2 = NULL;
	std::list<BindProto::bindele_t*>::iterator eit;
	for (eit = pb->eleList.begin(); eit != pb->eleList.end(); ++eit) {
		pele = *eit;
		if (pele->pcs) return false;
		pit = pm->find(pele->objdatana);
		if (pm->end() == pit) goto do_err;
		pd = pit->second;
		// 设定绑定
		p2 = new BindProto::Bind2ELE_t;
		p2->b = pb;
		p2->e = pele;
		node_cb_t ncb = {
		        _bind_nodeBeginCB, _bind_nodeEndCB, _bind_objUpdateCB,
		        _bind_buBornCB, _bind_buDeadCB, _bind_buUpdateCB,
			_bind_autoMachine, {0, 0},(void*)p2
		};
		// 生成csnode
		pele->pcs = csnode_buf_rent(pd, &ncb, NODE_CTL_NONE);
		pele->live = 1;
		if(!pele->pcs) goto do_err;
	}
	// 插入到绑定控制map中
	_bindSet.insert(pb);
	return true;
do_err:
	CSB_LOG("绑定失败了!!\n");
	// 绑定失败，清除内存
	while (eit != pb->eleList.begin()) {
		pele = *eit;
		csnode_buf_return(pele->pcs);
		pele->live = 0;
		pele->pcs = NULL;
		--eit;
	}
	return false;
}

void CSBBindMan::unbind(BindProto::bind_t* pb, bool rmBu, bool dofinal)
{
	// 判断是否绑定了这个结构
	std::set<BindProto::bind_t*>::iterator it = _bindSet.find(pb);
	CSB_YES_VRET(_bindSet.end() == it);
	// 如果解除绑定的时候内部存在任何一个暂停的元素那么将执行dofinal
	BindProto::bindele_t* e;
	for (std::list<BindProto::bindele_t*>::iterator eit = pb->eleList.begin();
	     eit != pb->eleList.end(); ++eit) {
		e = *eit;
		if (!CSB_FLAG_TEST(e->flag, CSB::BindProto::RUNNING)) {
			dofinal = true;
			break;
		}
	}
	pb->proto->unbindMe(pb, (rmBu ? BindProto::RM_BULLET_ATONCE : 0) |
			    (dofinal ? BindProto::FINAL : 0));

}

void CSBBindMan::unbindAll(bool rmBu, bool dofinal)
{
	BindProto::bind_t* pb = NULL;
	std::set<BindProto::bind_t*>::iterator it;
	int flag = (rmBu ? BindProto::RM_BULLET_ATONCE : 0) | (dofinal ? BindProto::FINAL : 0);
	for (it = _bindSet.begin(); it != _bindSet.end(); ++it) {
		pb = *it;
		pb->proto->unbindMe(pb, flag);
	}
}


void CSBBindMan::run(BindProto::bind_t* p, bool r, bool emitNow)
{
	if (p && !p->eleList.empty()) {
		// 有效性检测
		std::set<BindProto::bind_t*>::iterator pit = _bindSet.find(p);
		CSB_YES_VRET(_bindSet.end() == pit);
		// 遍历设置
		BindProto::bindele_t* e = NULL;
		if (r) {
			for (std::list<BindProto::bindele_t*>::iterator it = p->eleList.begin();
			     it != p->eleList.end(); ++it) {
				e = *it;
				e->flag = CSB_FLAG_ON(e->flag, CSB::BindProto::RUNNING);
				e->pcs->ctlflag = CSB_FLAG_TO(!emitNow, e->pcs->ctlflag, NODE_CTL_STOP_EMIT);
			}
		} else {
			for (std::list<BindProto::bindele_t*>::iterator it = p->eleList.begin();
			     it != p->eleList.end(); ++it) {
				e = *it;
				e->flag = CSB_FLAG_OFF(e->flag, CSB::BindProto::RUNNING);
				e->pcs->ctlflag = CSB_FLAG_TO(!emitNow, e->pcs->ctlflag, NODE_CTL_STOP_EMIT);
			}
		}
	}
}

void CSBBindMan::run(BindProto::bind_t* p, size_t n, bool r, bool emitNow)
{
	// 有效性检测
	std::set<BindProto::bind_t*>::iterator pit = _bindSet.find(p);
	CSB_YES_VRET(_bindSet.end() == pit);
	// 遍历设置
	BindProto::bindele_t* e = BindProto::getEleN(p, n);
	if (e){
		e->flag = CSB_FLAG_TO(r, e->flag, CSB::BindProto::RUNNING);
		e->pcs->ctlflag = CSB_FLAG_TO(!emitNow, e->pcs->ctlflag, NODE_CTL_STOP_EMIT);
	}
}

void CSBBindMan::kakaAllBulletsOnce()
{
	if (_bindSet.empty()) return;
	BindProto::bind_t* pb = NULL;
	for (std::set<BindProto::bind_t*>::iterator it = _bindSet.begin();
	     it != _bindSet.end();++it) {
		pb = *it;
		pb->proto->clearBulletsOnce(pb);
	}
}

NS_CSB_END
